package org.smartboot.flow.helper.view;

import org.smartboot.flow.core.attribute.AttributeHolder;
import org.smartboot.flow.core.common.ComponentType;
import org.smartboot.flow.core.util.AuxiliaryUtils;
import org.smartboot.flow.core.visitor.ComponentVisitor;
import org.smartboot.flow.core.visitor.ConditionVisitor;
import org.smartboot.flow.core.visitor.PipelineVisitor;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @author yamikaze
 * @date 2022/11/14
 * @since 1.0.5
 */
public class XmlComponentVisitor extends ComponentVisitor {

    private final List<AttributeHolder> attributes = new ArrayList<>();
    private final ComponentType type;
    private XmlPipelineVisitor pipeline;
    private String condition;
    private String branch;
    private final List<XmlComponentVisitor> components = new ArrayList<>();
    /**
     * 记录访问过的对象
     */
    private final Set<Object> visited;
    private String executable;
    private String script;

    public XmlComponentVisitor(ComponentType type, String name, String describe, Set<Object> visited) {
        this.type = type;
        this.visited = visited;
    }

    @Override
    public void visitExecutable(String executable) {
        this.executable = executable;
    }

    @Override
    public PipelineVisitor visitPipeline(String pipeline) {
        this.pipeline = new XmlPipelineVisitor(pipeline, visited);
        return this.pipeline;
    }

    @Override
    public ConditionVisitor visitCondition(String condition) {
        this.condition = condition;
        return new XmlConditionVisitor();
    }

    @Override
    public ComponentVisitor visitComponent(ComponentType type, String name, String describe) {
        XmlComponentVisitor component = new XmlComponentVisitor(type, name, describe, visited);
        this.components.add(component);
        return component;
    }

    @Override
    public void visitAttributes(List<AttributeHolder> attributes) {
        this.attributes.addAll(attributes);
    }

    @Override
    public ComponentVisitor visitBranch(Object branch, ComponentType type, String name, String describe) {
        XmlComponentVisitor component = new XmlComponentVisitor(type, name, describe, visited);
        component.branch = (String.valueOf(branch));
        this.components.add(component);
        return component;
    }

    private void appendAttributes(StringBuilder content) {
        for (AttributeHolder ah : attributes) {
            content.append(ah.getAttribute().getName()).append("=\"").append(ah.getValue().toString()).append("\" ");
        }
    }

    public void generate(StringBuilder content, int numbersOfTab) {
        ComponentType type = this.type;

        if (script != null) {
            ScriptCollector.collect(condition, script);
        }

        if (type == ComponentType.BASIC) {
            AuxiliaryUtils.appendTab(content, numbersOfTab);
            content.append("<component execute=\"").append(executable).append("\" ");
            appendAttributes(content);
            content.append("/>\n");
        } else if (type == ComponentType.IF) {
            AuxiliaryUtils.appendTab(content, numbersOfTab);
            content.append("<if test=\"").append(condition).append("\" ");
            appendAttributes(content);
            content.append(">\n");

            AuxiliaryUtils.appendTab(content, numbersOfTab + 1);
            content.append("<then>\n");
            components.get(0).generate(content, numbersOfTab + 2);
            AuxiliaryUtils.appendTab(content, numbersOfTab + 1);
            content.append("</then>\n");

            if (components.size() > 1) {
                AuxiliaryUtils.appendTab(content, numbersOfTab + 1);
                content.append("<else>\n");
                components.get(1).generate(content, numbersOfTab + 2);
                AuxiliaryUtils.appendTab(content, numbersOfTab + 1);
                content.append("</else>\n");
            }
            AuxiliaryUtils.appendTab(content, numbersOfTab);
            content.append("</if>\n");
        } else if (type == ComponentType.CHOOSE) {
            AuxiliaryUtils.appendTab(content, numbersOfTab);
            content.append("<choose test=\"").append(condition).append("\" ");
            appendAttributes(content);
            content.append(">\n");

            for (XmlComponentVisitor component : components) {
                if (component.branch != null) {
                    AuxiliaryUtils.appendTab(content, numbersOfTab + 1);
                    content.append("<case when=\"").append(component.branch).append("\" >\n");
                    component.generate(content, numbersOfTab + 2);
                    AuxiliaryUtils.appendTab(content, numbersOfTab + 1);
                    content.append("</case>\n");
                } else {
                    AuxiliaryUtils.appendTab(content, numbersOfTab + 1);
                    content.append("<default>\n");
                    component.generate(content, numbersOfTab + 2);
                    AuxiliaryUtils.appendTab(content, numbersOfTab + 1);
                    content.append("</default>\n");
                }
            }
            AuxiliaryUtils.appendTab(content, numbersOfTab);
            content.append("</choose>\n");

        } else if (type == ComponentType.PIPELINE) {
            if (pipeline.isCycle()) {
                AuxiliaryUtils.appendTab(content, numbersOfTab);
                content.append("<component subprocess=\"")
                        .append(pipeline.getName()).append("\" ");
                appendAttributes(content);
                content.append("/>\n");
                return;
            }

            pipeline.generate(content, numbersOfTab);
        } else if (type == ComponentType.ADAPTER) {
            AuxiliaryUtils.appendTab(content, numbersOfTab);
            content.append("<adapter execute=\"").append(executable).append("\" ");
            appendAttributes(content);
            content.append(">\n");
            XmlComponentVisitor pipeline = components.get(0);
            pipeline.pipeline.generate(content, numbersOfTab + 1);
            AuxiliaryUtils.appendTab(content, numbersOfTab);
            content.append("</adapter>\n");
        }
    }

    private class XmlConditionVisitor extends ConditionVisitor {

        @Override
        public void visitScript(String script) {
            XmlComponentVisitor.this.script = script;
        }
    }
}
